package com.gonsin.gmessage.logic.proxy.receiver;

import com.gonsin.gmessage.annotations.GArgsBody;
import com.gonsin.gmessage.annotations.GMessageEnvironment;
import com.gonsin.gmessage.annotations.GZoom;
import com.gonsin.gmessage.logic.model.GMessageModel;
import com.gonsin.gmessage.logic.model.TerminalInfo;
import com.gonsin.gmessage.logic.mqtt.MqttClientManager;
import com.gonsin.gmessage.logic.utils.ClassUtil;
import com.gonsin.gmessage.logic.utils.MessageModelTypeAdapter;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.mqttv5.client.IMqttMessageListener;
import org.eclipse.paho.mqttv5.common.MqttMessage;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.util.StringUtils;

import java.lang.reflect.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Slf4j
public class GMessageReceiverEndpoint implements BeanFactoryAware, GMessageCallback, IMqttMessageListener {

    /**
     * @GMessageReceiver 标注的方法
     */
    private Method method;

    /**
     * @GMessageReceiver 设置的firstCmd
     */
    private String firstCmd;

    /**
     * GMessageReceiver 设置的subCmd
     */
    private String subCmd;

    /**
     * spring bean 指向的实际的对象
     */
    private Object bean;

    /**
     * spring bean的唯一id
     */
    private String id;

    private BeanFactory beanFactory;

    private int qos;

    /**
     * MQTT v5新增特性，共享topic
     */
    private boolean share;

    /**
     * 是否有最后的 zoom
     */
    private boolean hasZoom;

    private MqttClientManager mqttClientManager;

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    public void setFirstCmd(String firstCmd) {
        this.firstCmd = firstCmd;
    }

    public void setSubCmd(String subCmd) {
        this.subCmd = subCmd;
    }

    public void setBean(Object bean) {
        this.bean = bean;
    }

    public void setMethod(Method method) {
        this.method = method;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    @Override
    public void onReceive(GMessageModel message) {

        boolean methodHasZoom = false;
        boolean messageHasZoom = false;

        // TODO 暂时根据有没有zoom，判断发送的目标
        if (!StringUtils.isEmpty(message.getZoom())) {
            messageHasZoom = true;
        }

        // 这里将接收到的数据转换成GMessageReceiver方法的参数
        int count = method.getParameterCount();
        Object[] paramsArray = new Object[count];
        method.getParameterTypes();

        Map<String, Object> props = message.getArgs();

        for (int i = 0; i < count; i++) {
            Class p = method.getParameterTypes()[i];

            if (TerminalInfo.class.isAssignableFrom(p)) {
                paramsArray[i] = message.getTerminal();
                continue;
            }


            GArgsBody body = ClassUtil.searchParamAnnotation(method, i, GArgsBody.class);
            if (body != null) {
                // 创建类转存表单
                Object searchPo = ClassUtil.map2Object(message.getArgs(), p);
                paramsArray[i] = searchPo;
                continue;
            }

            // 如果是基本数据类型
            if (ClassUtil.isBasicClass(p)) {

                GZoom zoomAnnotation = ClassUtil.searchParamAnnotation(method, i, GZoom.class);
                if (zoomAnnotation != null) {
                    paramsArray[i] = message.getZoom();
                    methodHasZoom = true;
                    if (messageHasZoom != methodHasZoom) {
                        return;
                    }
                } else {
                    String name = ClassUtil.getParamName(method, i);
                    Object value = ClassUtil.parseValue(props.get(name), p);
                    paramsArray[i] = value;
                    if (value == null) {
                        log.warn("获取到【{}】的值为空，请查看是否忘记填入@GSession标注或者@GParam标注", name);
                    }
                }
                continue;
            }


            /**
             * 获取参数名字
             */
            String paramName = ClassUtil.getParamName(method, i);
            Object value = props.get(paramName);
            if (value == null) {
                continue;
            }
            if (value instanceof Map) {
                Object searchPo = ClassUtil.map2Object((Map<String, Object>) value, p);
                paramsArray[i] = searchPo;
                continue;
            }


            if (Date.class.isAssignableFrom(p)) {

            }


            if (List.class.isAssignableFrom(p)) {
                if (!(value instanceof List)) {
                    log.warn("方法【{}】的参数【{}】不能接收，接收的类型是【{}】，但实际传进来的是【{}】", method.getName(), paramName, p.getSimpleName(), value.getClass().getSimpleName());
                    continue;
                }
                ParameterizedType paramType = (ParameterizedType) method.getGenericParameterTypes()[i];
                Type targetType = paramType.getActualTypeArguments()[0];
                if (!(targetType instanceof Class)) {
                    log.warn("方法【{}】的参数【{}】不能接收，因为不是实际类型", method.getName(), paramName);
                    continue;
                }
                Class targetClass = (Class) targetType;
                // 如果列表是基本类型
//                if(ClassUtil.isBasicClass(targetClass)){
//
//                }

                List list = (List) value;
                if (list.isEmpty()) {
                    paramsArray[i] = list;
                    continue;
                }

                List targetList = new ArrayList();
                if (ClassUtil.isBasicClass(targetClass)) {
                    for (int j = 0; j < list.size(); j++) {
                        Object targetItem = ClassUtil.convertToTargetType(list.get(j), targetClass);
                        targetList.add(targetItem);
                    }
                } else {
                    for (int j = 0; j < list.size(); j++) {
                        Object targetItem = ClassUtil.map2Object((Map<String, Object>) list.get(j), targetClass);
                        targetList.add(targetItem);
                    }
                }
                paramsArray[i] = targetList;
                continue;

            }


            // TODO 补充 数组类型
            if (p.isArray()) {
                String className = p.getName();
                String targetClassName = className.substring(2, className.length() - 1);
                if (!(value instanceof List)) {
                    log.warn("方法【{}】的参数【{}】不能接收，接收的类型是【{}】，但实际传进来的是【{}】", method.getName(), paramName, p.getSimpleName(), value.getClass().getSimpleName());
                    continue;
                }

//                ParameterizedType paramType = (ParameterizedType) method.getGenericParameterTypes()[i];
//                Type targetType = paramType.getActualTypeArguments()[0];
//                if(!(targetType instanceof Class)){
//                    log.warn("方法【{}】的参数【{}】不能接收，因为不是实际类型", method.getName(), paramName);
//                    continue;
//                }
                Class targetClass = null;
                try {
                    targetClass = Class.forName(targetClassName);
                } catch (ClassNotFoundException e) {
                    log.warn("方法【{}】的参数【{}】不能接收，不支持类型", method.getName(), paramName);
                    continue;
                }
                // 如果列表是基本类型
//                if(ClassUtil.isBasicClass(targetClass)){
//
//                }

                List list = (List) value;
                if (list.isEmpty()) {
                    paramsArray[i] = new Object[]{};
                    continue;
                }

                Object targetArray = Array.newInstance(targetClass, list.size());
                for (int j = 0; j < list.size(); j++) {
                    Object targetItem = ClassUtil.convertToTargetType(list.get(j), targetClass);
//                    targetArray[j] = targetItem;
                    Array.set(targetArray, j, targetItem);
                }
                paramsArray[i] = targetArray;
            }

        }

        try {
            if (messageHasZoom != methodHasZoom) {
                return;
            }
            method.invoke(bean, paramsArray);
        } catch (Exception e) {
            if (e instanceof InvocationTargetException) {
                Throwable ex = ((InvocationTargetException) e).getTargetException();
                log.error(ex.toString(), ex);
            } else {
                log.error(e.toString(), e);
            }
//            throw e;
        }

    }

    public String getFirstCmd() {
        return firstCmd;
    }

    public String getSubCmd() {
        return subCmd;
    }

    public void setQos(int qos) {
        this.qos = qos;
    }

    public void setShare(boolean share) {
        this.share = share;
    }

    public boolean isShare() {
        return share;
    }

    public int getQos() {
        return qos;
    }

    @Override
    public void messageArrived(String topic, MqttMessage message) throws Exception {

        try {
            String payload = new String(message.getPayload(), StandardCharsets.UTF_8);

            String[] cmds = topic.split("/");
            if (cmds == null || cmds.length < 3) {
                if (GMessageEnvironment.LOG) {
                    log.warn("收到无效topic=【{}】，payload=【{}】", topic, payload);
                }
                return;
            }

//        String firstCmd = cmds[1];
//        String subCmd = cmds[2];
            String zoom = cmds.length > 3 ? cmds[3] : null;
            if (topic.startsWith("$SHARE")) {
                zoom = null;
            }

//        Map<String, MessageContainer> map2 = containerMap.get(firstCmd);
//        if(map2 == null){
//            return;
//        }

//        MessageContainer container = map2.get(subCmd);
//        if(container == null){
//            return;
//        }

//        GMessageModel model = JSONObject.parseObject(payload, GMessageModel.class);
//        GMessageModel model = new Gson().fromJson(payload, GMessageModel.class);
            Gson gson = new GsonBuilder()
                    .registerTypeAdapter(new TypeToken<Map<String, Object>>() {
                    }.getType(), new MessageModelTypeAdapter())
                    .create();
            GMessageModel model = gson.fromJson(payload, GMessageModel.class);
            model.setZoom(zoom);
            onReceive(model);
        } catch (Exception ex) {
            log.error(ex.toString(), ex);
        }
    }

    public boolean isHasZoom() {
        return hasZoom;
    }

    public void setHasZoom(boolean hasZoom) {
        this.hasZoom = hasZoom;
    }
}
